/* 
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 2, or (at your option) 
 * any later version. 
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 
 * 02111-1307, USA. 
 * 
 * http://www.gnu.org/copyleft/gpl.html 
 */
package l1j.server.server;

public class Cipher {
	
	/**
	 * 혼동 비밀 번호와 혼동되는 * 난수 값 (32 비트, 정적 읽기 전용)이 값은 암호화에만있다가 호출됩니다 초기화됩니다
	 */
	private final static int _1 = 0x9c30d539;
	/**
	 * 초기 디코딩 값
	 */
	private final static int _2 = 0x930fd7e2;
	/**
	 * 혼동 비밀 번호와 혼동되는 * 난수 값 (32 비트, 정적 읽기 전용)이 값은 암호화에만있다가 호출됩니다 초기화됩니다
	 */
	private final static int _3 = 0x7c72e993;
	/**
	 * 혼동 암호 패킷 값 혼란 (32 비트, 정적 읽기 전용)만 인코딩하거나 디코딩의 값이 호출됩니다
	 */
	private final static int _4 = 0x287effc3;

	
	/**
	 * 레퍼런스 코드 키 (8 바이트 배열의 길이, 64 비트 정수의 이에 상응하는 값)
	 */
	private final byte[] eb = new byte[8];
	/**
	 * 디코딩 키를 참조 (바이트 배열의 길이가 8이고, 64 비트 정수 (Long)에 상응)
	 */
	private final byte[] db = new byte[8];
	/**
	 * 참고 패킷 키 (바이트 배열의 길이, 32 비트 정수의 이에 상응하는 금액)
	 */
	private final byte[] tb = new byte[4];

	// 초기화 과정 :
	// 새 키 레지스터 (키) 작성 키와 혼란 키 (_1), 혼란을 코딩과 열쇠를 가져올 것입니다 [0] 키를 [1]로 디코딩의 초기 값
	// 키 [0] 19 비트 (0x13)을 반대 권리 및 핵심에 [0]
	// 키 [0]와 키 [1]과 키 (_3), 혼란과 키 [1]로 혼동
	// 키는 64 비트 바이트 배열로 변환
	// @ PARAM 난수에 의해 생성된 키를 암호화 키
	/**
	 * 지정된 암호 키를 사용하여 새 Cipher 오브젝트를 작성합니다.
	 *
	 * @ param key
	 * 암호화에 사용되는 32bit의 암호화 키
	 */
	public Cipher(int key) {
		int[] keys = { key ^ _1, _2 };
		keys[0] = Integer.rotateLeft(keys[0], 0x13);
		keys[1] ^= keys[0] ^ _3;

		for (int i = 0; i < keys.length; i++) {
			for (int j = 0; j < tb.length; j++) {
				eb[(i * 4) + j] = db[(i * 4) + j] = (byte) (keys[i] >> (j * 8) & 0xff);
			}
		}
	}

	/**
	 * 지정된 byte 배열을 암호화합니다.
	 *
	 * @ param data
	 * 암호화 byte 배열.
	 * @ return 암호화 된 byte 배열입니다. 이것은 매개 변수의 byte 배열과 동일한 객체입니다.
	 */
	public byte[] encrypt(byte[] data) {
		for (int i = 0; i < tb.length; i++) {
			tb[i] = data[i];
		}

		data[0] ^= eb[0];

		for (int i = 1; i < data.length; i++) {
			data[i] ^= data[i - 1] ^ eb[i & 7];
		}

		data[3] ^= eb[2];
		data[2] ^= eb[3] ^ data[3];
		data[1] ^= eb[4] ^ data[2];
		data[0] ^= eb[5] ^ data[1];
		update(eb, tb);
		return data;
	}

	/**
	 * 지정된 byte 배열을 복호화합니다.
	 *
	 * @ param data
	 * 해독하는 byte 배열입니다.
	 * @ return 암호화된 byte 배열. 이것은 매개 변수의 byte 배열과 동일한 객체입니다.
	 */
	public byte[] decrypt(byte[] data) {
		data[0] ^= db[5] ^ data[1];
		data[1] ^= db[4] ^ data[2];
		data[2] ^= db[3] ^ data[3];
		data[3] ^= db[2];

		for (int i = data.length - 1; i >= 1; i--) {
			data[i] ^= data[i - 1] ^ db[i & 7];
		}

		data[0] ^= db[0];
		update(db, data);
		return data;
	}

	/**
	 * 지정된 키를 혼란과 합계 혼란 키 (_4)
	 *
	 * @ PARAM 데이터
	 * 보호된 정보
	 * @ 반환 데이터, 원본 데이터
	 */
	private void update(byte[] data, byte[] ref) {
		for (int i = 0; i < tb.length; i++) {
			data[i] ^= ref[i];
		}

		int int32 = (((data[7] & 0xFF) << 24) | ((data[6] & 0xFF) << 16)
				| ((data[5] & 0xFF) << 8) | (data[4] & 0xFF))
				+ _4;

		for (int i = 0; i < tb.length; i++) {
			data[i + 4] = (byte) (int32 >> (i * 8) & 0xff);
		}
	}
}